﻿using System;

namespace Wheels
{
    /// <summary>
    /// 提供一些关于 DateTime 的转换方法。
    /// </summary>
    public static class DateTimeExtensions
    {
        private static readonly DateTime smWorldBegin = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        /// <summary>
        /// 计算出给定时间点在 ECMAScript 中的等价数值。
        /// </summary>
        public static double ToECMAScriptTicks(this DateTime value)
        {
            // ECMAScript 中的时间刻度实际上是自世界协调时1970年1月1日0时整开始计算的毫秒数。
            // 进行转换的时候只要得出两个时间的差值并转换成毫秒数就可以了。

            if (DateTimeKind.Local == value.Kind)
                value = value.ToUniversalTime();

            return (value - smWorldBegin).TotalMilliseconds;
        }

        /// <summary>
        /// 计算出给定时间点在 ECMAScript 中的等价数值，可为 null。
        /// </summary>
        public static double? ToECMAScriptTicks(this DateTime? value)
        {
            // 主要挑战是 null，除此之外并没有什么特别需要注意的地方。

            if (value.HasValue)
                return value.Value.ToECMAScriptTicks();
            return null;
        }

        #region ByGivenPrecision

        /// <summary>
        /// 传换成具有给定精度的时间点。
        /// </summary>
        public static DateTime ByGivenPrecision(this DateTime value, long ticks)
        {
            // 先除后乘，舍弃掉不足的部分。

            return new DateTime(value.Ticks / ticks * ticks, value.Kind);
        }
        /// <summary>
        /// 转换成具有给定精度的时间点，可为 null。
        /// </summary>
        public static DateTime? ByGivenPrecision(this DateTime? value, long ticks)
        {
            // 同上。

            if (value.HasValue)
                return new DateTime(value.Value.Ticks / ticks * ticks, value.Value.Kind);
            return null;
        }

        /// <summary>
        /// 传换成具有给定精度的时间点。
        /// </summary>
        public static DateTime ByGivenPrecision(this DateTime value, long ticks, MidpointRounding mode)
        {
            // 先除后乘，舍弃掉不足的部分。

            decimal time = value.Ticks;
            time = time / ticks;

            return new DateTime((long)Math.Round(time, mode) * ticks, value.Kind);
        }
        /// <summary>
        /// 转换成具有给定精度的时间点，可为 null。
        /// </summary>
        public static DateTime? ByGivenPrecision(this DateTime? value, long ticks, MidpointRounding mode)
        {
            // 同上。

            if (value.HasValue)
                return value.Value.ByGivenPrecision(ticks, mode);
            //return new DateTime(value.Value.Ticks / ticks * ticks, value.Value.Kind);
            return null;
        }

        #endregion

        #region BySecond

        /// <summary>
        /// 将时间精度降低到以秒为单位。
        /// </summary>
        public static DateTime BySecond(this DateTime value)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerSecond);
        }
        /// <summary>
        /// 将时间精度降低到以秒为单位，可为 null。
        /// </summary>
        public static DateTime? BySecond(this DateTime? value)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerSecond);
        }

        /// <summary>
        /// 将时间精度降低到以秒为单位。
        /// </summary>
        public static DateTime BySecond(this DateTime value, MidpointRounding mode)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerSecond, mode);
        }
        /// <summary>
        /// 将时间精度降低到以秒为单位，可为 null。
        /// </summary>
        public static DateTime? BySecond(this DateTime? value, MidpointRounding mode)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerSecond, mode);
        }

        #endregion

        #region ByMillisecond

        /// <summary>
        /// 将时间精度降低到以毫秒为单位。
        /// </summary>
        public static DateTime ByMillisecond(this DateTime value)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerMillisecond);
        }
        /// <summary>
        /// 将时间精度降低到以毫秒为单位，可为 null。
        /// </summary>
        public static DateTime? ByMillisecond(this DateTime? value)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerMillisecond);
        }

        /// <summary>
        /// 将时间精度降低到以毫秒为单位。
        /// </summary>
        public static DateTime ByMillisecond(this DateTime value, MidpointRounding mode)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerMillisecond, mode);
        }
        /// <summary>
        /// 将时间精度降低到以毫秒为单位，可为 null。
        /// </summary>
        public static DateTime? ByMillisecond(this DateTime? value, MidpointRounding mode)
        {
            return value.ByGivenPrecision(TimeSpan.TicksPerMillisecond, mode);
        }

        #endregion

    }
}
